import { EOF, Parser } from './parser';
import { ChunkedBodyParser } from './chunkedBodyParser';

export class ResponseParser extends Parser {
  protected state = this.WAITING_STATUS_LINE;

  private statusLine: string = '';

  private headers: Record<string, unknown> = {};

  private headerName: string = '';
  private headerValue: string = '';

  private bodyParser: null | ChunkedBodyParser = null;
  private bodyContent: string = '';

  get response() {
    this.statusLine.match(/HTTP\/1.1 (\d+) ([\s\S]+)/);

    return {
      statusCode: RegExp.$1,
      statusText: RegExp.$2,
      headers: this.headers,
      body: this.bodyParser?.content.join('') ?? '',
    };
  }

  private WAITING_STATUS_LINE(char: string | typeof EOF) {
    switch (char) {
      case '\r': {
        return this.WAITING_STATUS_LINE_END;
      }

      default: {
        this.statusLine += char.toString();
        return this.WAITING_STATUS_LINE;
      }
    }
  }

  private WAITING_STATUS_LINE_END(char: string | typeof EOF) {
    switch (char) {
      case '\n': {
        return this.WAITING_HEADER_NAME;
      }
    }
  }

  private WAITING_HEADER_NAME(char: string | typeof EOF) {
    switch (char) {
      case ':': {
        return this.WAITING_HEADER_SPACE;
      }

      case '\r': {
        if (this.headers['Transfer-Encoding'] === 'chunked') {
          this.bodyParser = new ChunkedBodyParser();
        }

        return this.WAITING_HEADER_BLOCK_END;
      }

      default: {
        this.headerName += char.toString();
        return this.WAITING_HEADER_NAME;
      }
    }
  }

  private WAITING_HEADER_SPACE(char: string | typeof EOF) {
    switch (char) {
      case ' ': {
        return this.WAITING_HEADER_VALUE;
      }
    }
  }

  private WAITING_HEADER_VALUE(char: string | typeof EOF) {
    switch (char) {
      case '\r': {
        this.headers[this.headerName] = this.headerValue;
        this.headerName = '';
        this.headerValue = '';
        return this.WAITING_HEADER_LINE_END;
      }

      default: {
        this.headerValue += char.toString();
        return this.WAITING_HEADER_VALUE;
      }
    }
  }

  private WAITING_HEADER_LINE_END(char: string | typeof EOF) {
    switch (char) {
      case '\n': {
        return this.WAITING_HEADER_NAME;
      }
    }
  }

  private WAITING_HEADER_BLOCK_END(char: string | typeof EOF) {
    switch (char) {
      case '\n': {
        return this.WAITING_BODY;
      }
    }
  }

  private WAITING_BODY(char: string | typeof EOF) {
    if (char === EOF) {
      this.bodyParser?.parse(this.bodyContent);
      return this.END;
    }
    
    if (typeof char === 'string') {
      this.bodyContent += char;
      return this.WAITING_BODY;
    }
  }

  private END(char: string | typeof EOF) {
    return;
  }
}